package com.symaster.common.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 文件工具类
 *
 * @author yinmiao
 * @date 2021-06-25 09:46
 */
public class FileUtil {

    public static String getTempFilePath(String filePath) {
        File file = new File(filePath);
        if (!file.isFile()) {
            return filePath;
        }
        return getTempPath(filePath);
    }

    private static String getTempPath(String path) {
        File file = new File(path);
        String name = file.getName();
        if (name.startsWith(".")) {
            return getTempPathByEndWith(path);
        }
        File parentFile = file.getParentFile();
        if (parentFile == null || StringUtils.isEmpty(parentFile.getPath())) {
            parentFile = new File("." + File.separator);
        }
        return parentFile.getPath() + File.separator + ".temp_" + name;
    }

    private static String getTempPathByEndWith(String path) {
        File file = new File(path);
        return file.getPath() + "_#temp#";
    }

    /**
     * 返回文件MD5值
     */
    public static String getMD5Checksum(String filePathName) throws NoSuchAlgorithmException, IOException {
        return getMD5Checksum(Files.newInputStream(Paths.get(filePathName)));
    }

    /**
     * 返回文件MD5值
     */
    public static String getMD5Checksum(InputStream is) throws NoSuchAlgorithmException, IOException {

        MessageDigest complete; // 如果想使用SHA-1或SHA-256，则传入SHA-1,SHA-256
        try {
            byte[] buffer = new byte[1024];
            complete = MessageDigest.getInstance("MD5");
            int numRead;

            do {
                numRead = is.read(buffer); // 从文件读到buffer，最多装满buffer
                if (numRead > 0) {
                    complete.update(buffer, 0, numRead); // 用读到的字节进行MD5的计算，第二个参数是偏移量
                }
            } while (numRead != -1);
        } finally {
            IOUtil.close(is);
        }

        byte[] digest = complete.digest();
        StringBuilder result = new StringBuilder();
        for (byte b : digest) {
            result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));// 加0x100是因为有的b[i]的十六进制只有1位
        }

        return result.toString();
    }

    /**
     * 返回md5值
     */
    public static String getMD5Checksum(byte[] bytes) throws NoSuchAlgorithmException {
        MessageDigest complete = MessageDigest.getInstance("MD5");
        complete.update(bytes, 0, bytes.length);
        byte[] digest = complete.digest();
        StringBuilder result = new StringBuilder();
        for (byte b : digest) {
            result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));// 加0x100是因为有的b[i]的十六进制只有1位
        }
        return result.toString();
    }

    /**
     * 返回可写临时文件目录
     */
    public static String getTempFolder(String applicationName) {
        return System.getProperty("java.io.tmpdir") + File.separator + applicationName;
    }

    /**
     * 复制文件
     */
    public static boolean copyFile(File src, File dst) {
        if (!src.isFile()) {
            throw new IllegalArgumentException(String.format("'%s'指定文件不存在", src.getPath()));
        }

        File parentFile = dst.getParentFile();
        if (!parentFile.isDirectory() && !parentFile.mkdirs()) {
            throw new IllegalArgumentException(String.format("'%s'目标文件不可读", dst.getPath()));
        }

        if (dst.isFile()) {
            boolean delete = dst.delete();
        }

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(Files.newInputStream(src.toPath()));
            bos = new BufferedOutputStream(Files.newOutputStream(dst.toPath()));
            IOUtil.write(bis, bos);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            IOUtil.close(bis, bos);
        }
        return true;
    }

    /**
     * 复制文件
     */
    public static boolean copyFile(String src, String dst) {
        return copyFile(new File(src), new File(dst));
    }

    /**
     * 删除此目录以及此目录下的所有文件
     */
    public static boolean deleteFolder(String filePath) {

        File file = new File(filePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
                for (File file1 : files) {
                    if (file1.isDirectory()) {
                        boolean b = deleteFolder(file1.getPath());
                    } else {
                        boolean delete = file1.delete();
                    }
                }
            }
        }
        boolean delete = file.delete();
        return true;
    }

    /**
     * 返回文件类型
     *
     * @param filePathName 文件路径或文件名
     * @return jpg ...
     */
    public static String getFileType(String filePathName) {
        int i = filePathName.lastIndexOf(".");
        if (i == -1) {
            return null;
        }
        return filePathName.substring(i + 1).toLowerCase();
    }

    /**
     * 读取JSON文件
     *
     * @param filePathName 文件路径
     * @param clazz        格式化Json
     * @param <T>          Object
     */
    public static <T> T readJson(String filePathName, Class<T> clazz) {
        return readJSON(filePathName, clazz, "UTF-8");
    }

    public static <T> T readJSON(String filePathName, Class<T> clazz, String charsetName) {
        JSONObject jsonObject = readJson(filePathName, charsetName);
        return jsonObject.toJavaObject(clazz);
    }

    /**
     * 读取JSON文件
     *
     * @param filePathName 文件路径
     */
    public static JSONObject readJson(String filePathName) {
        return readJson(filePathName, "UTF-8");
    }

    public static JSONObject readJson(String filePathName, String charsetName) {
        File file = new File(filePathName);
        if (!file.isFile()) {
            throw new RuntimeException(String.format("%s 文件不存在", file.getPath()));
        }

        StringBuilder sb = null;
        BufferedReader bufferedReader = null;
        try {
            sb = new StringBuilder();
            bufferedReader = new BufferedReader(new InputStreamReader(Files.newInputStream(file.toPath()), charsetName));
            String line;
            while ((line = bufferedReader.readLine()) != null) sb.append(line);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            IOUtil.close(bufferedReader);
        }

        return JSONObject.parseObject(sb.toString());
    }

    /**
     * 输出为JSON文件
     *
     * @param filePathName 文件路径
     * @param obj          Java对象
     */
    public static void writeJson(String filePathName, Object obj) {

        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(Paths.get(filePathName))));
            bw.write(JSONObject.toJSONString(obj));
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            IOUtil.close(bw);
        }
    }

    /**
     * 判断两个文件是否相同
     */
    public static boolean equalsFile(String f1, String f2) {
        return equalsFile(new File(f1), new File(f2));
    }

    /**
     * 判断两个文件是否相同
     */
    public static boolean equalsFile(File f1, File f2) {
        if (!f1.isFile() || !f2.isFile()) {
            return false;
        }

        FileInputStream fis1 = null;
        FileInputStream fis2 = null;
        try {
            fis1 = new FileInputStream(f1);
            fis2 = new FileInputStream(f2);
            byte[] bytes1 = IOUtil.toByteArray(fis1);
            byte[] bytes2 = IOUtil.toByteArray(fis2);
            if (bytes1 == null || bytes2 == null) {
                return false;
            }
            if (bytes1.length != bytes2.length) {
                return false;
            }
            for (int i = 0; i < bytes1.length; i++) {
                if (bytes1[i] != bytes2[i]) {
                    return false;
                }
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            IOUtil.close(fis1, fis2);
        }

        return true;
    }

    // Obtain a logger instance
    private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);

    public static void delete(String fileName) {
        delete(fileName, "");
    }

    public static void delete(String fileName, String fileExt) {
        File file = new File(fileName);
        delete(file, fileExt);
    }

    public static String getNameWithoutExtension(File file) {
        return file.getName().replaceFirst("[.][^.]+$", "");
    }

    // Delete the file or if it's a directory, all files in the directory
    public static void delete(File file, final String fileExt) {
        if (file.exists()) {
            // check if the file is a directory
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    // call deletion of file individually
                    delete(f, fileExt);
                }
            } else {
                String path = file.getAbsolutePath();
                if (fileExt != null && path.endsWith(fileExt)) {
                    boolean result = file.delete();
                    // test if delete of file is success or not
                    if (result) {
                        LOGGER.info("File {} deleted", file.getAbsolutePath());
                    } else {
                        LOGGER.info("File {} was not deleted, unknown reason", file.getAbsolutePath());
                    }
                }
            }
        } else {
            LOGGER.info("File {} doesn't exist", file.getAbsolutePath());
        }
    }

    public static List<String> list(String dir) {
        return list(dir, "");
    }

    // List all files in a directory
    public static List<String> list(String dir, String fileExt) {
        File file = new File(dir);
        return list(file, fileExt);
    }

    // List all files in a directory with the specified extension
    public static List<String> list(File dir, final String fileExt) {
        //
        List<String> fileList = new ArrayList<String>();

        // For all files and folders in directory
        if (dir.isDirectory()) {
            // Get a list of all files and folders in directory
            File[] files = dir.listFiles();
            for (File f : files) {
                // Recursively call file list function on the new directory
                list(f, fileExt);
            }
        } else {
            // If not directory, print the file path and add it to return list
            String path = "";
            try {
                path = dir.getCanonicalPath();
            } catch (IOException e) {
                LOGGER.error("IOException", e);
            }
            if (fileExt != null && path.endsWith(fileExt)) {
                fileList.add(path);
                LOGGER.info("File: {}", path);
            }
        }

        return fileList;
    }

    // From: stackoverflow.com
    public static File[] listFilesMatching(File root, String regex) {
        if (!root.isDirectory()) {
            throw new IllegalArgumentException(root + " is not a directory.");
        }
        final Pattern p = Pattern.compile(regex);
        return root.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return p.matcher(file.getName()).matches();
            }
        });
    }
}
